
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
// 
//   http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

/**
 * AUTO-GENERATED FILE. DO NOT MODIFY.
 */

// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
// 
//   http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.
import { parseSVG, makeViewBoxTransform } from 'zrender/lib/tool/parseSVG.js';
import Group from 'zrender/lib/graphic/Group.js';
import Rect from 'zrender/lib/graphic/shape/Rect.js';
import { assert, createHashMap, each } from 'zrender/lib/core/util.js';
import BoundingRect from 'zrender/lib/core/BoundingRect.js';
import { parseXML } from 'zrender/lib/tool/parseXML.js';
import { GeoSVGRegion } from './Region.js';
/**
 * "region available" means that: enable users to set attribute `name="xxx"` on those tags
 * to make it be a region.
 * 1. region styles and its label styles can be defined in echarts opton:
 * ```js
 * geo: {
 *     regions: [{
 *         name: 'xxx',
 *         itemStyle: { ... },
 *         label: { ... }
 *     }, {
 *         ...
 *     },
 *     ...]
 * };
 * ```
 * 2. name can be duplicated in different SVG tag. All of the tags with the same name share
 * a region option. For exampel if there are two <path> representing two lung lobes. They have
 * no common parents but both of them need to display label "lung" inside.
 */

var REGION_AVAILABLE_SVG_TAG_MAP = createHashMap(['rect', 'circle', 'line', 'ellipse', 'polygon', 'polyline', 'path', // <text> <tspan> are also enabled because some SVG might paint text itself,
// but still need to trigger events or tooltip.
	'text', 'tspan', // <g> is also enabled because this case: if multiple tags share one name
	// and need label displayed, every tags will display the name, which is not
	// expected. So we can put them into a <g name="xxx">. Thereby only one label
	// displayed and located based on the bounding rect of the <g>.
	'g']);

var GeoSVGResource =
/** @class */
function () {
	function GeoSVGResource(mapName, svg) {
		this.type = 'geoSVG'; // All used graphics. key: hostKey, value: root

		this._usedGraphicMap = createHashMap(); // All unused graphics.

		this._freedGraphics = [];
		this._mapName = mapName; // Only perform parse to XML object here, which might be time
		// consiming for large SVG.
		// Although convert XML to zrender element is also time consiming,
		// if we do it here, the clone of zrender elements has to be
		// required. So we do it once for each geo instance, util real
		// performance issues call for optimizing it.

		this._parsedXML = parseXML(svg);
	}

	GeoSVGResource.prototype.load = function ()
	/* nameMap: NameMap */
	{
		// In the "load" stage, graphic need to be built to
		// get boundingRect for geo coordinate system.
		var firstGraphic = this._firstGraphic; // Create the return data structure only when first graphic created.
		// Because they will be used in geo coordinate system update stage,
		// and `regions` will be mounted at `geo` coordinate system,
		// in which there is no "view" info, so that it should better not to
		// make references to graphic elements.

		if (!firstGraphic) {
			firstGraphic = this._firstGraphic = this._buildGraphic(this._parsedXML);

			this._freedGraphics.push(firstGraphic);

			this._boundingRect = this._firstGraphic.boundingRect.clone(); // PENDING: `nameMap` will not be supported until some real requirement come.
			// if (nameMap) {
			//     named = applyNameMap(named, nameMap);
			// }

			var _a = createRegions(firstGraphic.named),
				regions = _a.regions,
				regionsMap = _a.regionsMap;

			this._regions = regions;
			this._regionsMap = regionsMap;
		}

		return {
			boundingRect: this._boundingRect,
			regions: this._regions,
			regionsMap: this._regionsMap
		};
	};

	GeoSVGResource.prototype._buildGraphic = function (svgXML) {
		var result;
		var rootFromParse;

		try {
			result = svgXML && parseSVG(svgXML, {
				ignoreViewBox: true,
				ignoreRootClip: true
			}) || {};
			rootFromParse = result.root;
			assert(rootFromParse != null);
		} catch (e) {
			throw new Error('Invalid svg format\n' + e.message);
		} // Note: we keep the covenant that the root has no transform. So always add an extra root.

		var root = new Group();
		root.add(rootFromParse);
		root.isGeoSVGGraphicRoot = true; // [THE_RULE_OF_VIEWPORT_AND_VIEWBOX]
		//
		// Consider: `<svg width="..." height="..." viewBox="...">`
		// - the `width/height` we call it `svgWidth/svgHeight` for short.
		// - `(0, 0, svgWidth, svgHeight)` defines the viewport of the SVG, or say,
		//   "viewport boundingRect", or `boundingRect` for short.
		// - `viewBox` defines the transform from the real content ot the viewport.
		//   `viewBox` has the same unit as the content of SVG.
		//   If `viewBox` exists, a transform is defined, so the unit of `svgWidth/svgHeight` become
		//   different from the content of SVG. Otherwise, they are the same.
		//
		// If both `svgWidth/svgHeight/viewBox` are specified in a SVG file, the transform rule will be:
		// 0. `boundingRect` is `(0, 0, svgWidth, svgHeight)`. Set it to Geo['_rect'] (View['_rect']).
		// 1. Make a transform from `viewBox` to `boundingRect`.
		//    Note: only support `preserveAspectRatio 'xMidYMid'` here. That is, this transform will preserve
		//    the aspect ratio.
		// 2. Make a transform from boundingRect to Geo['_viewRect'] (View['_viewRect'])
		//    (`Geo`/`View` will do this job).
		//    Note: this transform might not preserve aspect radio, which depending on how users specify
		//    viewRect in echarts option (e.g., `geo.left/top/width/height` will not preserve aspect ratio,
		//    but `geo.layoutCenter/layoutSize` will preserve aspect ratio).
		//
		// If `svgWidth/svgHeight` not specified, we use `viewBox` as the `boundingRect` to make the SVG
		// layout look good.
		//
		// If neither `svgWidth/svgHeight` nor `viewBox` are not specified, we calculate the boundingRect
		// of the SVG content and use them to make SVG layout look good.

		var svgWidth = result.width;
		var svgHeight = result.height;
		var viewBoxRect = result.viewBoxRect;
		var boundingRect = this._boundingRect;

		if (!boundingRect) {
			var bRectX = void 0;
			var bRectY = void 0;
			var bRectWidth = void 0;
			var bRectHeight = void 0;

			if (svgWidth != null) {
				bRectX = 0;
				bRectWidth = svgWidth;
			} else if (viewBoxRect) {
				bRectX = viewBoxRect.x;
				bRectWidth = viewBoxRect.width;
			}

			if (svgHeight != null) {
				bRectY = 0;
				bRectHeight = svgHeight;
			} else if (viewBoxRect) {
				bRectY = viewBoxRect.y;
				bRectHeight = viewBoxRect.height;
			} // If both viewBox and svgWidth/svgHeight not specified,
			// we have to determine how to layout those element to make them look good.

			if (bRectX == null || bRectY == null) {
				var calculatedBoundingRect = rootFromParse.getBoundingRect();

				if (bRectX == null) {
					bRectX = calculatedBoundingRect.x;
					bRectWidth = calculatedBoundingRect.width;
				}

				if (bRectY == null) {
					bRectY = calculatedBoundingRect.y;
					bRectHeight = calculatedBoundingRect.height;
				}
			}

			boundingRect = this._boundingRect = new BoundingRect(bRectX, bRectY, bRectWidth, bRectHeight);
		}

		if (viewBoxRect) {
			var viewBoxTransform = makeViewBoxTransform(viewBoxRect, boundingRect); // Only support `preserveAspectRatio 'xMidYMid'`

			rootFromParse.scaleX = rootFromParse.scaleY = viewBoxTransform.scale;
			rootFromParse.x = viewBoxTransform.x;
			rootFromParse.y = viewBoxTransform.y;
		} // SVG needs to clip based on `viewBox`. And some SVG files really rely on this feature.
		// They do not strictly confine all of the content inside a display rect, but deliberately
		// use a `viewBox` to define a displayable rect.
		// PENDING:
		// The drawback of the `setClipPath` here is: the region label (genereted by echarts) near the
		// edge might also be clipped, because region labels are put as `textContent` of the SVG path.

		root.setClipPath(new Rect({
			shape: boundingRect.plain()
		}));
		var named = [];
		each(result.named, function (namedItem) {
			if (REGION_AVAILABLE_SVG_TAG_MAP.get(namedItem.svgNodeTagLower) != null) {
				named.push(namedItem);
				setSilent(namedItem.el);
			}
		});
		return {
			root: root,
			boundingRect: boundingRect,
			named: named
		};
	};
	/**
   * Consider:
   * (1) One graphic element can not be shared by different `geoView` running simultaneously.
   *     Notice, also need to consider multiple echarts instances share a `mapRecord`.
   * (2) Converting SVG to graphic elements is time consuming.
   * (3) In the current architecture, `load` should be called frequently to get boundingRect,
   *     and it is called without view info.
   * So we maintain graphic elements in this module, and enables `view` to use/return these
   * graphics from/to the pool with it's uid.
   */

	GeoSVGResource.prototype.useGraphic = function (hostKey
		/* , nameMap: NameMap */
	) {
		var usedRootMap = this._usedGraphicMap;
		var svgGraphic = usedRootMap.get(hostKey);

		if (svgGraphic) {
			return svgGraphic;
		}

		svgGraphic = this._freedGraphics.pop() // use the first boundingRect to avoid duplicated boundingRect calculation.
    || this._buildGraphic(this._parsedXML);
		usedRootMap.set(hostKey, svgGraphic); // PENDING: `nameMap` will not be supported until some real requirement come.
		// `nameMap` can only be obtained from echarts option.
		// The original `named` must not be modified.
		// if (nameMap) {
		//     svgGraphic = extend({}, svgGraphic);
		//     svgGraphic.named = applyNameMap(svgGraphic.named, nameMap);
		// }

		return svgGraphic;
	};

	GeoSVGResource.prototype.freeGraphic = function (hostKey) {
		var usedRootMap = this._usedGraphicMap;
		var svgGraphic = usedRootMap.get(hostKey);

		if (svgGraphic) {
			usedRootMap.removeKey(hostKey);

			this._freedGraphics.push(svgGraphic);
		}
	};

	return GeoSVGResource;
}();

export { GeoSVGResource };

function setSilent(el) {
	// Only named element has silent: false, other elements should
	// act as background and has no user interaction.
	el.silent = false; // text|tspan will be converted to group.

	if (el.isGroup) {
		el.traverse(function (child) {
			child.silent = false;
		});
	}
}

function createRegions(named) {
	var regions = [];
	var regionsMap = createHashMap(); // Create resions only for the first graphic.

	each(named, function (namedItem) {
		// Region has feature to calculate center for tooltip or other features.
		// If there is a <g name="xxx">, the center should be the center of the
		// bounding rect of the g.
		if (namedItem.namedFrom != null) {
			return;
		}

		var region = new GeoSVGRegion(namedItem.name, namedItem.el); // PENDING: if `nameMap` supported, this region can not be mounted on
		// `this`, but can only be created each time `load()` called.

		regions.push(region); // PENDING: if multiple tag named with the same name, only one will be
		// found by `_regionsMap`. `_regionsMap` is used to find a coordinate
		// by name. We use `region.getCenter()` as the coordinate.

		regionsMap.set(namedItem.name, region);
	});
	return {
		regions: regions,
		regionsMap: regionsMap
	};
} // PENDING: `nameMap` will not be supported until some real requirement come.
// /**
//  * Use the alias in geoNameMap.
//  * The input `named` must not be modified.
//  */
// function applyNameMap(
//     named: GeoSVGGraphicRecord['named'],
//     nameMap: NameMap
// ): GeoSVGGraphicRecord['named'] {
//     const result = [] as GeoSVGGraphicRecord['named'];
//     for (let i = 0; i < named.length; i++) {
//         let regionGraphic = named[i];
//         const name = regionGraphic.name;
//         if (nameMap && nameMap.hasOwnProperty(name)) {
//             regionGraphic = extend({}, regionGraphic);
//             regionGraphic.name = name;
//         }
//         result.push(regionGraphic);
//     }
//     return result;
// }